/******************************************************************************

	COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2010.

	This file is part of Maxsi Engine.

	Maxsi Engine is free software: you can redistribute it and/or modify it
	under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or (at your
	option) any later version.

	Maxsi engine is distributed in the hope that it will be useful, but WITHOUT
	ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
	FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
	for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with Maxsi Engine. If not, see <http://www.gnu.org/licenses/>.

	MaxsiEngine.dll
	A general purpose C++ library for the Maxsi Technology Project.

	MaxsiIO.cpp
	A Simple Flexible Async I/O System.

******************************************************************************/

#include "MaxsiEngine.h"

#ifdef Maxsi_Support_MaxsiIO

BeginMaxsiNamespace

#ifdef THIS_ISN_T_DEF_D
int TestMaxsiIO()
{
/*

	A > B
	B < A

		Streaming operator. A is connected with B in normal mode.

	A >> B
	B << A

		Reverse streaming operator. A is connected with B in reverse mode.

	A / B
	B \ A

		One way streaming operator. A is connected with B in normal mode, but
		data can only flow from A to B.

	A // B
	B \\ A

		One way reverse streaming operator. A is connected with N in reverse
		mode, but data can only flow from A to B.

	A & B > C
	C > ( A > B )

		Stream to last stream operator. A is connected with B. When the
		connection between A and B is closed, then C is connected to B.

	A + B > C

		Append stream operator. A is connection to C and when A closes, B
		seemlessly takes over and continues the connection.

	C > A + B
	C >> A + B
	
		Fork stream operator. Connects C to A and when the connection is closed
		then C is connected to B.
	

	C > A{B} > D

		File access operator. If A supports a virtual file system, then the
		file B is located. C is written to the file and then the contents are
		forwarded to D.

	A[C, D] > D

		Stream range operator. Data in the range C to D of A is connected to D.

	B < A | C

		Split operator. A is connected to B in a one-way mode that only allows A
		to write to B. C is also connected to B, and all write operations from B
		go to C.
	
*/

/*

	"http://www.maxsi.dk/" > HTTP > $MaxsiFront

	$MaxsiFront[200,300] > StdOut

*/

/*
	StdIn > HTTP > Bin2Hex > Compress > StdOut

	MaxsiIO://StdIn>HTTP>Bin2Hex>Compress>StdOut

*/

/*

	!"#¤%&/()=?+|;:-,<>\

	$TorrentUrl = "?server=http://www.maxsi.dk/&product=Altered%20Transmission";

	($TorrentUrl > MaxsiTorrent > $AT) {"Altered Transmission/GameInfo.txt"} > StdOut;

	$AT{"Altered Transmission/GameInfo.txt"} > Deflate > FileSystem{./CompressedFile};

	FileSystem{./CompressedFile} >> Deflate > FileSystem{./DecompressedFile};
	
*/

/*

	StdInOut-based chat system:

	Server:
	MaxsiIO://"192.168.2.6:1337" > Listen >> Deflate > StdInOut;

	Client:
	MaxsiIO://"192.168.2.6:1337" > TCP >> Deflate > StdInOut;

*/

/*

	StdInOut > Deflate > FileSystem{"CompressedLog.bin"};

*/

/*

	MaxsiIO://FileSystem{"Output.gz"} < gzip < ($CombinedFile < ( ( FileSystem{"MyFile.txt"} + FileSystem{AnotherFile.footer}[2048, 2048] ) ) [4096] + $CombinedFile + ( Force < FileSystem{"FileReadNoMatterWhat.txt"} ) );

*/

/*

	MaxsiIO:// ( FileSystem{MyImage.jpg} > JPG2BITMAP > RescaleImage(200, 200) ) & ( RescaleImage(200, 200) < PNG2BITMAP < FileSystem{Overlay.png} ) > OverlayImages >> PNG2BITMAP > FileSystem{Result.png} >> JPEG2PNG > FTP{ftp.maxsi.dk/TestImage.png};

*/

/*

	MaxsiIO://HTTP<"http://www.maxsi.dk/">

*/

/*
	
	FileSystem{Archive.tar.gz} >> gzip >> Tar > $Folder > ("[A-Z]*" > FilterFolder) > FileSystem{"./"}

	MaxsiIO://FileSystem{"Archive.tar.gz"]>>Tarball>("[A-Z]*">FilterFolder)>FileSystem{"./"}

*/

/*

	MaxsiIO:// FileSystem{File.bin} > ("sortie.maxsi.dk:80" > TCP)

*/

/*

	MaxsiIO:// FileSystem{./} > (StdOut < "Please enter the files (RegExp): "; StdInLine > FilterFolder) > Tarball > FileSystem{./Compressed.tar.gz}

*/

/*

	MaxsiIO:// ("sortie.maxsi.dk:31337" > TCP) < Deflate < FileSystem{"Input.bin"} | FileSystem{"Output.bin"}

*/

/*

	MaxsiIO:// FileSystem{Input.txt} > FileSystem{Output.txt} + StdOut + ("sortie.maxsi.dk:1337" > TCP)

*/

/*

	MaxsiIO:// $Default < (HTTP < "http://www.maxsi.dk/index.php") : "index.php" & (Tarball << HTTP < "http://www.maxsi.dk/bigfile.tar.gz") : "Subfolder/";

	MaxsiIO:// FileSystem{"Test.mio"} > MaxsiIO > FileSystem;

*/

	MaxsiIODeviceEpoll*	Network	=	new MaxsiIODeviceEpoll;
	MaxsiIOHTTP*		HTTP	=	new MaxsiIOHTTP;
	MaxsiIOBin2Hex*		Bin2Hex	=	new	MaxsiIOBin2Hex;
	MaxsiIOStdOut*		StdOut	=	new MaxsiIOStdOut;

	MaxsiError			Error;

	Bin2Hex->AsyncOpenDevice(&Bin2Hex->IO, StdOut);
	Bin2Hex->AsyncOpenConnection(&Bin2Hex->II, HTTP, &HTTP->OO);

	HTTP->AsyncOpenURL(&Output->I, Network, "http://www.maxsi.dk/")

	Network->MainLoop();

	return 1;
}
#endif

//=============================================================================
//	Locks.
//=============================================================================
MaxsiHandle IOConnectivity_Lock = NULL;

MaxsiError IOInitLockConnectivity()
{
	if ( IOConnectivity_Lock == NULL )
	{
		IOConnectivity_Lock		=	ThreadSystem()->CreateCriticalSection();
	}

	return (IOConnectivity_Lock != NULL) ? MAXSI_ERROR_SUCCESS : MAXSI_ERROR_UNSPECIFIED;
}

MaxsiError IODestructLockConnectivity()
{
	if ( IOConnectivity_Lock != NULL )
	{
		ThreadSystem()->DeleteCriticalSection(IOConnectivity_Lock);
		IOConnectivity_Lock		=	NULL;		
	}

	return MAXSI_ERROR_SUCCESS;	
}

class InitializeIOConnectivity_Lock
{
public:
	InitializeIOConnectivity_Lock() { IOInitLockConnectivity(); }
	~InitializeIOConnectivity_Lock() { IODestructLockConnectivity(); }
};
 
InitializeIOConnectivity_Lock Autorun_IOConnectivity_Lock;

MaxsiError IOLockConnectivity()
{
	if ( IOConnectivity_Lock == NULL ) { return MAXSI_ERROR_NOT_INITIALIZED; }	

	ThreadSystem()->EnterCriticalSection(IOConnectivity_Lock);

	return MAXSI_ERROR_SUCCESS;	
}

MaxsiError IOUnlockConnectivity()
{
	if ( IOConnectivity_Lock == NULL ) { return MAXSI_ERROR_NOT_INITIALIZED; }	

	ThreadSystem()->LeaveCriticalSection(IOConnectivity_Lock);

	return MAXSI_ERROR_SUCCESS;
}


//=============================================================================
//	MaxsiIODevice.
//=============================================================================

FLAGSL32 MaxsiIODevice::GetCapabilities()
{
	return ( 0 );
}

MaxsiIOHandle* MaxsiIODevice::AllocateIOHandle()
{
	return new MaxsiIOHandle;
}

MaxsiError MaxsiIODevice::DeleteIOHandle(MaxsiIOHandle* Handle)
{
	delete Handle;
	return MAXSI_ERROR_SUCCESS;
}

MaxsiError MaxsiIODevice::MainLoop()
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError MaxsiIODevice::HandleOpenDevice(MaxsiIOEntity* Remote, MaxsiIOSocket* Socket, Word_t Direction)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError MaxsiIODevice::HandleListen(MaxsiIOEntity* Remote, MaxsiIOSocket* Socket, short Port)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError MaxsiIODevice::HandleConnect(MaxsiIOEntity* Remote, MaxsiIOSocket* Socket, MESTR* HostName, short Port)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError MaxsiIODevice::HandleOpenFile(MaxsiIOEntity* Remote, MaxsiIOSocket* Socket, MESTR* Path, FLAGS32 Mode)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}
 
MaxsiError MaxsiIODevice::HandleAccept(MaxsiIOEntity* Remote, MaxsiIOSocket* Socket, MaxsiIOSocket ListenSocket)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError MaxsiIODevice::HandleDisconnect(MaxsiIOSocket* Socket)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError MaxsiIODevice::SetUpAsyncIOHandleCallbacks(MaxsiIOSocket* Connection)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError MaxsiIODevice::AttemptRead(MaxsiIOHandle* Connection, BYTE* Buffer, size_t* BufferSize, size_t* Transferred)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError MaxsiIODevice::AttemptWrite(MaxsiIOHandle* Connection, BYTE* Buffer, size_t* BufferSize, size_t* Transferred)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

//=============================================================================
//	MaxsiIOEntity.
//=============================================================================

FLAGSL32 MaxsiIOEntity::GetCapabilities()
{
	return ( MAXSI_IO_SUPPORTS_VIRTUAL_SOCKETS | MAXSI_IO_SUPPORTS_NON_BLOCKING | MAXSI_IO_SUPPORTS_NORMAL_MODE | MAXSI_IO_SUPPORTS_REVERSE_MODE );
}

MaxsiError MaxsiIOEntity::CreateBuffers()
{
	return MAXSI_ERROR_SUCCESS;
}

MaxsiError MaxsiIOEntity::DeleteBuffers()
{
	return MAXSI_ERROR_SUCCESS;
}

MaxsiError MaxsiIOEntity::CreateBuffers(MaxsiIOSocket* Socket)
{
	return MAXSI_ERROR_SUCCESS;
}

MaxsiError MaxsiIOEntity::DeleteBuffers(MaxsiIOSocket* Socket)
{
	return MAXSI_ERROR_SUCCESS;
}

FLAGSL32 MaxsiIOEntity::GetSocketCapabilities(MaxsiIOSocket* Socket)
{
	if ( Socket == &NI ) { return ( MAXSI_IO_SOCKET_IS_INCOMING | MAXSI_IO_SOCKET_IS_NORMAL ); } else
	if ( Socket == &RI ) { return ( MAXSI_IO_SOCKET_IS_INCOMING | MAXSI_IO_SOCKET_IS_NORMAL ); } else
	if ( Socket == &NO ) { return ( MAXSI_IO_SOCKET_IS_OUTGOING | MAXSI_IO_SOCKET_IS_REVERSE ); } else
	if ( Socket == &RO ) { return ( MAXSI_IO_SOCKET_IS_OUTGOING | MAXSI_IO_SOCKET_IS_REVERSE ); }

	return 0;
}

size_t MaxsiIOEntity::GetSocketEndpoints(MaxsiIOSocket* Socket, MaxsiIOSocket** Output, size_t OutputLength)
{
	if ( Socket == NULL ) { return SIZE_MAX; }
	if ( Output == NULL ) { return 1; }
	if ( OutputLength < 1 ) { return 0; }

	if ( Socket == &NI ) { Output[0] = &NO; return 1; } else
	if ( Socket == &NO ) { Output[0] = &NI; return 1; } else
	if ( Socket == &RI ) { Output[0] = &RO; return 1; } else
	if ( Socket == &RO ) { Output[0] = &RI; return 1; }

	return SIZE_MAX;
}

size_t MaxsiIOEntity::GetIncomingSockets(MaxsiIOSocket** Output, size_t OutputLength)
{
	if ( Output == NULL ) { return 2; }

	if ( OutputLength < 1 ) { return 0; } Output[0] = &NI;
	if ( OutputLength < 2 ) { return 1; } Output[0] = &RI;

	return 2;
}

size_t MaxsiIOEntity::GetOutgoingSockets(MaxsiIOSocket** Output, size_t OutputLength)
{
	if ( Output == NULL ) { return 2; }

	if ( OutputLength < 1 ) { return 0; } Output[0] = &NO;
	if ( OutputLength < 2 ) { return 1; } Output[0] = &RO;

	return 2;
}

MaxsiIOSocket* MaxsiIOEntity::GetNormalIncoming()
{
	return &NI;
}

MaxsiIOSocket* MaxsiIOEntity::GetNormalOutgoing()
{
	return &NO;
}

MaxsiIOSocket* MaxsiIOEntity::GetReverseIncoming()
{
	return &RI;
}

MaxsiIOSocket* MaxsiIOEntity::GetReverseOutgoing()
{
	return &RO;
}

MaxsiError MaxsiIOEntity::NotifyOpen(MaxsiIOSocket* Socket, FLAGSL32 State)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError MaxsiIOEntity::NotifyClose(MaxsiIOSocket* Socket, FLAGSL32 State)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError MaxsiIOEntity::NotifyRead(MaxsiIOSocket* Socket, FLAGSL32 State)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError NotifyWrite(MaxsiIOSocket* Socket, FLAGSL32 State)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError MaxsiIOEntity::NotifyIncomingConnection(MaxsiIOSocket* Socket, FLAGSL32 State)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

MaxsiError MaxsiIOEntity::ReadConnection(MaxsiIOHandle* Connection, MaxsiBuffer* Buffer)
{
	return MAXSI_ERROR_NOT_SUPPORTED;
}

//=============================================================================
//	Connectivity functions.
//=============================================================================
MaxsiError IOConnectToDevice(MaxsiIOEntity* From, MaxsiIOSocket* FromSocket, MaxsiIODevice* Device, Word_t Direction)
{
	// Lock the connectivity, so only this thread is allowed to edit connections.
	IOLockConnectivity();

	MaxsiError	Result	=	Device->HandleOpenDevice(From, FromSocket, Direction),
	
	IOUnlockConnectivity();

	return Result;
}

MaxsiError IOConnectToFileSystem(MaxsiIOEntity* From, MaxsiIOSocket* FromSocket, MaxsiIODevice* Device, MESTR* Path, FLAGS32 Mode)
{
	// Lock the connectivity, so only this thread is allowed to edit connections.
	IOLockConnectivity();

	MaxsiError	Result	=	Device->HandleOpenFile(From, FromSocket, Path, Mode),
	
	IOUnlockConnectivity();

	return Result;
}

MaxsiError IOListenOnDevice(MaxsiIOEntity* From, MaxsiIOSocket* FromSocket, MaxsiIODevice* Device, short Port)
{
	// Lock the connectivity, so only this thread is allowed to edit connections.
	IOLockConnectivity();

	MaxsiError	Result	=	Device->HandleListen(From, FromSocket, Port),
	
	IOUnlockConnectivity();

	return Result;
}

MaxsiError IOAcceptFromDevice(MaxsiIOEntity* Dest, MaxsiIOSocket* DestSocket, MaxsiIODevice* Device, MaxsiIOSocket ListenSocket)
{
	// Lock the connectivity, so only this thread is allowed to edit connections.
	IOLockConnectivity();

	MaxsiError	Result	=	Device->HandleAccept(Dest, DestSocket, ListenSocket),
	
	IOUnlockConnectivity();

	return Result;
}

MaxsiError IOConnectToSocket(MaxsiIOEntity* From, MaxsiIOSocket* FromSocket, MaxsiIOEntity* Dest, MaxsiIOSocket* DestSocket)
{
	// Check if we were passed invalid input.
	if ( From == NULL || FromSocket == NULL || Dest == NULL || DestSocket == NULL ) { return MAXSI_ERROR_INVALID_INPUT; }

	// Lock the connectivity, so only this thread is allowed to edit connections.
	IOLockConnectivity();

	// Check if our sockets are already connected.
	if ( *DestSocket || *FromSocket ) { IOUnlockConnectivity(); return MAXSI_ERROR_ALREADY_CONNECTED; }

	// Get the features that our connections implement.
	FLAGSL32	FromCapabilities		=	From->GetCapabilities();
	FLAGSL32	DestCapabilities		=	Dest->GetCapabilities();

	// Check if they support virtual sockets.
	if ( !(FromCapabilities & MAXSI_IO_SUPPORTS_VIRTUAL_SOCKETS) && !(DestCapabilities & MAXSI_IO_SUPPORTS_VIRTUAL_SOCKETS) ) { IOUnlockConnectivity(); return MAXSI_ERROR_NOT_SUPPORTED; }

	// Get information about our sockets.
	FLAGSL32	FromSockCapabilities	=	From->GetSocketCapabilities(FromSocket);
	FLAGSL32	DestSockCapabilities	=	Dest->GetSocketCapabilities(DestSocket);

	// Check if they support being bound this way.
	if ( !(FromSockCapabilities & MAXSI_IO_SOCKET_IS_OUTGOING) && !(DestSockCapabilities & MAXSI_IO_SOCKET_IS_INCOMING)) { IOUnlockConnectivity(); return MAXSI_ERROR_NOT_SUPPORTED; }
	
	// Attempt to allocate an IO Handle that will store connection specific information.
	MaxsiIOHandle*	FromHandle	=	From->AllocateIOHandle();

	// Check if the handle was correctly created, if not then clean up.
	if ( FromHandle == NULL ) { IOUnlockConnectivity(); return MAXSI_ERROR_MEM_ALLOC_FAIL; }
	
	// Attempt to allocate an IO Handle that will store connection specific information.
	MaxsiIOHandle*	DestHandle	=	Dest->AllocateIOHandle();
	
	// Check if the handle was correctly created, if not then clean up.
	if ( DestHandle == NULL ) { From->DeleteIOHandle(FromHandle); IOUnlockConnectivity(); return MAXSI_ERROR_MEM_ALLOC_FAIL; }

	// Set the from socket state information.
	FromHandle->Connection		=	From;
	FromHandle->Device			=	Dest;
	FromHandle->Socket			=	FromSocket;
	FromHandle->Direction		=	MAXSI_IO_DIRECTION_WRITE;

	// Set the dest socket state information.
	DestHandle->Connection		=	Dest;
	DestHandle->Device			=	From;
	DestHandle->Socket			=	DestSocket;
	DestHandle->Direction		=	MAXSI_IO_DIRECTION_READ;

	// Assign the IO Handles to the sockets.
	*FromSocket					=	FromHandle;
	*DestSocket					=	DestHandle;

	MaxsiError	Result			=	MAXSI_ERROR_SUCCESS;

	// Notify the connections of the success connection.
	if ( Result == MAXSI_ERROR_SUCCESS ) { Result = From->NotifyOpen(FromSocket, MAXSI_IO_SOCKET_IS_OUTGOING); }
	if ( Result == MAXSI_ERROR_SUCCESS ) { Result = Dest->NotifyOpen(DestSocket, MAXSI_IO_SOCKET_IS_INCOMING); }

	// In case one of the connections encountered internal issues, simply clean up and return its error code.
	if ( Result != MAXSI_ERROR_SUCCESS )
	{
		// Disconnect the sockets.
		From->NotifyClose(FromSocket, MAXSI_IO_SOCKET_IS_OUTGOING);
		Dest->NotifyClose(FromSocket, MAXSI_IO_SOCKET_IS_INCOMING);
		*FromSocket					=	NULL;
		*DestSocket					=	NULL;
		delete FromHandle;
		delete DestHandle;		
	}

	IOUnlockConnectivity(); 

	return Result;
}

MaxsiError IOConnectNormalOneWay(MaxsiIOEntity* From, MaxsiIOEntity* Dest)
{
	// Check if we were passed invalid input.
	if ( From == NULL || Dest == NULL ) { return MAXSI_ERROR_INVALID_INPUT; }

	// Lock the connectivity, so only this thread is allowed to edit connections.
	IOLockConnectivity(); 

	// Find the default sockets. 
	MaxsiIOSocket*	FromSock	=	From->GetNormalOutgoing();
	MaxsiIOSocket*	DestSock	=	Dest->GetNormalIncoming();

	MaxsiError	Result	=	IOConnectToSocket(From, FromSock, Dest, DestSock);

	IOUnlockConnectivity();

	return Result;
}

MaxsiError IOConnectReverseOneWay(MaxsiIOEntity* From, MaxsiIOEntity* Dest)
{
	// Check if we were passed invalid input.
	if ( From == NULL || Dest == NULL ) { return MAXSI_ERROR_INVALID_INPUT; }

	// Lock the connectivity, so only this thread is allowed to edit connections.
	IOLockConnectivity(); 

	// Find the default sockets. 
	MaxsiIOSocket*	FromSock	=	From->GetReverseOutgoing();
	MaxsiIOSocket*	DestSock	=	Dest->GetReverseIncoming();

	MaxsiError	Result	=	IOConnectToSocket(From, FromSock, Dest, DestSock);

	IOUnlockConnectivity();

	return Result;
}

MaxsiError IOConnectNormalTwoWay(MaxsiIOEntity* From, MaxsiIOEntity* Dest)
{
	// Check if we were passed invalid input.
	if ( From == NULL || Dest == NULL ) { return MAXSI_ERROR_INVALID_INPUT; }

	// Lock the connectivity, so only this thread is allowed to edit connections.
	IOLockConnectivity(); 

	// Find the default sockets. 
	MaxsiIOSocket*	FromSockNormal	=	From->GetNormalOutgoing();
	MaxsiIOSocket*	DestSockNormal	=	Dest->GetNormalIncoming();
	MaxsiIOSocket*	FromSockReverse	=	From->GetReverseIncoming();
	MaxsiIOSocket*	DestSockReverse	=	Dest->GetReverseOutgoing();

	MaxsiError	Result		=	MAXSI_ERROR_SUCCESS;

	Result = IOConnectToSocket(From, FromSockNormal, Dest, DestSockNormal);

	if ( Result == MAXSI_ERROR_SUCCESS )
	{
		Result = IOConnectToSocket(Dest, DestSockReverse, From, FromSockReverse);

		// In case the second connection failed, simply close the first successful connection.
		if ( Result != MAXSI_ERROR_SUCCESS ) { IODisconnectSocket(FromSockNormal); }		
	}

	IOUnlockConnectivity();

	return Result;
}

MaxsiError IOConnectReverseTwoWay(MaxsiIOEntity* From, MaxsiIOEntity* Dest)
{
	// Check if we were passed invalid input.
	if ( From == NULL || Dest == NULL ) { return MAXSI_ERROR_INVALID_INPUT; }

	// Lock the connectivity, so only this thread is allowed to edit connections.
	IOLockConnectivity(); 

	// Find the default sockets. 
	MaxsiIOSocket*	FromSockNormal	=	From->GetNormalOutgoing(); // ANO
	MaxsiIOSocket*	DestSockNormal	=	Dest->GetNormalIncoming(); // ARI
	MaxsiIOSocket*	FromSockReverse	=	From->GetReverseIncoming(); // BNO
	MaxsiIOSocket*	DestSockReverse	=	Dest->GetReverseOutgoing(); // BRI

	MaxsiError	Result		=	MAXSI_ERROR_SUCCESS;

	Result = IOConnectToSocket(Dest, DestSockNormal, From, FromSockNormal);

	if ( Result == MAXSI_ERROR_SUCCESS )
	{
		Result = IOConnectToSocket(From, FromSockReverse, Dest, DestSockReverse);

		// In case the second connection failed, simply close the first successful connection.
		if ( Result != MAXSI_ERROR_SUCCESS ) { IODisconnectSocket(DestSockNormal); }		
	}

	IOUnlockConnectivity();

	return Result;
}

MaxsiError IODisconnectSocket(MaxsiIOSocket* Socket)
{
	if ( Socket == NULL ) { return MAXSI_ERROR_INVALID_INPUT; }

	// Lock the connectivity, so only this thread is allowed to edit connections.
	IOLockConnectivity(); 

	// Check if the socket actually is bound.
	if ( (*Socket) != NULL )
	{
		// Check if a device is set.
		if ( (*Socket)->Device )
		{
			// Notify the device of the closure.
			(*Socket)->Device->HandleDisconnect(Socket);
		}

		// Check if a connection is set and notify the end-point of the closure.
		if ( (*Socket)->Connection ) { (*Socket)->Connection->NotifyClose(Socket, 0); }
	}

	IOUnlockConnectivity(); 
	
	return MAXSI_ERROR_SUCCESS;
}

MaxsiError IODeleteSocket(MaxsiIOSocket Socket)
{
	delete (MaxsiIOHandle*)Socket;
	return MAXSI_ERROR_SUCCESS;
}

//=============================================================================
//	MaxsiIOEntity - Initializers and destructors.
//=============================================================================

MaxsiIOEntity::MaxsiIOEntity()
{
	NIBuf	=	NULL;
	NOBuf	=	NULL;
	RIBuf	=	NULL;
	ROBuf	=	NULL;
	NI		=	NULL;
	NO		=	NULL;
	RI		=	NULL;
	RO		=	NULL;
}

MaxsiIOEntity::~MaxsiIOEntity()
{
	
}

//=============================================================================
//	MaxsiIOEntity - IO transfer functions.
//=============================================================================

#if 0
MaxsiError MaxsiIOEntity::ReadConnection(MaxsiIOHandle* Connection, MaxsiBuffer* Buffer)
{
	if ( Connection == NULL || Buffer == NULL || Connection->Device == NULL ) { return MAXSI_ERROR_INVALID_INPUT; }

	MaxsiError	Result		=	MAXSI_ERROR_SUCCESS;
	BYTE*		Data		=	NULL;
	size_t		DataLength	=	0;

	Buffer->Lock( MAXSI_BUFFER_LOCK_PARAMS );

	// Check if the buffer is full.
	if ( Result == MAXSI_ERROR_SUCCESS && Buffer->GetBufferSize() == Buffer->GetBufferUsed() )
	{
		Result = MAXSI_ERROR_OUT_OF_MEM;
	}

	// Check if the buffer is already in use.
	if ( Result == MAXSI_ERROR_SUCCESS && Buffer->IsInUse() )
	{
		Result = MAXSI_ERROR_IS_IN_USE;
	}

	if ( Result == MAXSI_ERROR_SUCCESS )
	{
		Data		=	Buffer->GetPointer(); MAXSI_TODO("Check if Data == NULL");

		Data		=	Buffer->ProgressPointer(Data, Buffer->GetBufferUsed()); MAXSI_TODO("Check if Data == NULL");

		DataLength	=	Buffer->GetPointerSizeFollowingData(); MAXSI_TODO("Check if DataLength == 0");
	
		// Set the buffer as being in use.
		Buffer->SetInUse();
	}
	
	Buffer->Unlock();

	if ( Result == MAXSI_ERROR_SUCCESS )
	{
		size_t		BufferSize	=	DataLength;
		size_t		Transferred	=	0;

		MaxsiError	ReadResult	=	Connection->Device->AttemptRead();
	}	

	return Result;
}
#endif

EndMaxsiNamespace

#endif

